package com.gitee.wsl.compose.animation.transition

import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VisibilityThreshold
import androidx.compose.animation.core.spring
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOut
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.runtime.Stable
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import com.gitee.wsl.compose.animation.spec.DefaultSpec
import kotlin.math.roundToInt

//缩放进入动画。该动画会使对象逐渐从较小的大小缩放到正常大小。
@Stable
fun Transition.In.slide(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffset: (fullSize: IntSize) -> IntOffset, // 初始的偏移值, 其中 fullSize 是整个控件的大小
) = slideIn(animationSpec,initialOffset)

//缩放离开动画。该动画会使对象逐渐从正常大小缩放到较小的大小。
@Stable
fun Transition.Out.slide(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffset: (fullSize: IntSize) -> IntOffset,
) = slideOut(animationSpec,initialOffset)

//水平滑入动画。该动画会使对象从给定方向的屏幕外滑入，直到达到正常位置。
@Stable
fun Transition.In.slideHorizontally(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffsetX: (fullWidth: Int) -> Int = { -it / 2 },
) = slideInHorizontally(animationSpec,initialOffsetX)

//水平滑出动画。该动画会使对象从正常位置滑出至给定方向的屏幕外。
@Stable
fun Transition.Out.slideHorizontally(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffsetX: (fullWidth: Int) -> Int = { -it / 2 },
) = slideOutHorizontally(animationSpec,initialOffsetX)

//垂直滑入动画。该动画会使对象从给定方向的屏幕外滑入，直到达到正常位置。
@Stable
fun Transition.In.slideVertically(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffsetX: (fullWidth: Int) -> Int = { -it / 2 },
) = slideInVertically(animationSpec,initialOffsetX)

//垂直滑出动画。该动画会使对象从正常位置滑出至给定方向的屏幕外。
@Stable
fun Transition.Out.slideVertically(
    animationSpec: FiniteAnimationSpec<IntOffset> =
        spring(
            stiffness = Spring.StiffnessMediumLow,
            visibilityThreshold = IntOffset.VisibilityThreshold
        ),
    initialOffsetX: (fullWidth: Int) -> Int = { -it / 2 },
) = slideOutVertically(animationSpec,initialOffsetX)


/**
 * Positive [itemHeightOffsetFraction] means the item is moving **up**.
 */
@Stable
fun Transition.In.slideInVertically(
    itemHeightOffsetFraction: Float,
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInVertically(spec) { (it * itemHeightOffsetFraction).roundToInt() }

/**
 * Positive [itemHeightOffsetFraction] means the item is moving **down**.
 */
@Stable
fun Transition.Out.slideOutVertically(
    itemHeightOffsetFraction: Float,
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): ExitTransition = slideOutVertically(spec) { (it * itemHeightOffsetFraction).roundToInt() }


/**
 * Returns an exit transition that slides the element to the bottom fully
 */
@Stable
fun Transition.Out.slideToBottom(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): ExitTransition = slideOutVertically(1f, spec)

/**
 * Returns an exit transition that slides the element to the top fully
 */
@Stable
fun Transition.Out.slideToTop(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): ExitTransition = slideOutVertically(-1f, spec)

/**
 * Returns an enter transition that slides the element from the bottom fully
 */
@Stable
fun Transition.In.slideFromBottom(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInVertically(1f, spec)

/**
 * Returns an enter transition that slides the element from the top fully
 */
@Stable
fun Transition.In.slideFromTop(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInVertically(-1f, spec)

/**
 *  A positive value means sliding from right to left, whereas a negative value would slide the content from left to right.
 */
@Stable
fun Transition.In.slideInHorizontally(
    widthOffsetFraction: Float,
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInHorizontally(spec) { (it * widthOffsetFraction).roundToInt() }

/**
 * A positive value means sliding to the right, whereas a negative value would slide the content towards the left.
 */
@Stable
fun Transition.Out.slideOutHorizontally(
    widthOffsetFraction: Float,
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): ExitTransition = slideOutHorizontally(spec) { (it * widthOffsetFraction).roundToInt() }

/**
 * Returns an enter transition that slides the element from the left fully
 */
@Stable
fun Transition.In.slideFromLeft(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInHorizontally(-1f, spec)

/**
 * Returns an enter transition that slides the element from the right fully
 */
@Stable
fun Transition.In.slideFromRight(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInHorizontally(1f, spec)

/**
 * Returns an exit transition that slides the element to the left fully
 */
@Stable
fun Transition.Out.slideToLeft(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): ExitTransition = slideOutHorizontally(-1f, spec)

/**
 * Returns an exit transition that slides the element to the right fully
 */
@Stable
fun Transition.In.slideToRight(
    spec: FiniteAnimationSpec<IntOffset> = DefaultSpec
): EnterTransition = slideInHorizontally(1f, spec)
